﻿using System;
using System.Collections.Generic;

namespace ZyGame.Game
{
    public class FSMEntry
    {
        public Entity entity { get; set; }

        private List<FSMState> states = new List<FSMState>();

        public FSMState current { get; private set; }

        public Type currentType { get; private set; }

        public FSMEntry(Entity entity)
        {
            this.entity = entity;
        }

        public bool HasState(string name)
        {
            return states.Exists(x => x.name == name);
        }

        public bool HasState(Type type)
        {
            return states.Exists(x => x.GetType() == type);
        }

        public bool HasState<T>() where T : FSMState
        {
            return HasState(typeof(T));
        }

        public bool HasState(FSMState state)
        {
            return HasState(state.GetType());
        }

        public bool Register(FSMState state)
        {
            if (HasState(state.name))
            {
                return false;
            }

            states.Add(state);
            state.Awake(entity);
            return true;
        }

        public FSMState Register(Type type)
        {
            if (!typeof(FSMState).IsAssignableFrom(type))
            {
                return default;
            }

            FSMState state = (FSMState)Activator.CreateInstance(type);
            if (!Register(state))
            {
                return default;
            }

            return state;
        }

        public T Register<T>() where T : FSMState, new()
        {
            return (T)Register(typeof(T));
        }

        public void Unregister(Type type)
        {
            FSMState data = states.Find(x => x.GetType() == type);
            if (data is null)
            {
                return;
            }

            Unregister(data);
        }

        public void Unregister(string name)
        {
            FSMState data = states.Find(x => x.name == name);
            if (data is null)
            {
                return;
            }

            Unregister(data);
        }

        public void Unregister<T>() where T : FSMState
        {
            Unregister(typeof(T));
        }

        public void Unregister(FSMState state)
        {
            if (states.Contains(state) is false)
            {
                return;
            }

            state.Dispose();
            states.Remove(state);
        }

        public FSMState GetState(string name)
        {
            return states.Find(x => x.name == name);
        }

        public FSMState GetState(Type type)
        {
            return states.Find(x => x.GetType() == type);
        }

        public T GetState<T>() where T : FSMState
        {
            return (T)GetState(typeof(T));
        }

        public void Switch(string name)
        {
            if (current.name == name)
            {
                return;
            }

            FSMState data = GetState(name);
            if (data is null)
            {
                return;
            }

            if (current is not null)
            {
                current.Exit();
            }

            Client.Console.WriteLine($"switch [{entity.guid}] fsm state [{current?.name}] to [{data.name}]");
            current = data;
            current.Execut();
            currentType = data.GetType();
        }

        public bool Switch(Type type)
        {
            if (currentType == type)
            {
                return false;
            }

            FSMState data = GetState(type);
            if (data is null)
            {
                return false;
            }

            if (current is not null)
            {
                current.Exit();
            }

            Client.Console.WriteLine($"switch [{entity.guid}] fsm state [{current?.name}] to [{data.name}]");
            current = data;
            current.Execut();
            currentType = type;
            return true;
        }

        public void Dispose()
        {
            states.ForEach(x => x.Dispose());
            states.Clear();
            current = null;
            currentType = null;
            GC.SuppressFinalize(this);
        }
    }
}